import { ref, computed, readonly } from 'vue'
import { defineStore } from 'pinia'
import type { ConnectionState } from '@/types'
import { rsocketService, ConnectionStatus, type ConnectionStatusEvent } from '@/services/rsocket'
import { useAuthStore } from './auth'
import { useMessagesStore } from './messages'
import { chatStorage } from '@/utils/storage'

export const useConnectionStore = defineStore('connection', () => {
  // State
  const isConnected = ref(false)
  const isConnecting = ref(false)
  const isReconnecting = ref(false)
  const reconnectAttempts = ref(0)
  const maxReconnectAttempts = ref(5)
  const lastConnectedAt = ref<Date | null>(null)
  const lastDisconnectedAt = ref<Date | null>(null)
  const error = ref<string | null>(null)
  const connectionQuality = ref<'good' | 'poor' | 'offline'>('offline')
  const offlineMessages = ref<Array<{ id: string; data: any; timestamp: Date }>>([])
  
  // Connection status listener cleanup function
  let statusListenerCleanup: (() => void) | null = null

  // Getters
  const connectionStatus = computed(() => {
    if (isConnecting.value) return 'connecting'
    if (isReconnecting.value) return 'reconnecting'
    if (isConnected.value) return 'connected'
    return 'disconnected'
  })

  const canRetry = computed(() => {
    return reconnectAttempts.value < maxReconnectAttempts.value
  })

  const connectionInfo = computed(() => ({
    status: connectionStatus.value,
    isConnected: isConnected.value,
    reconnectAttempts: reconnectAttempts.value,
    maxReconnectAttempts: maxReconnectAttempts.value,
    lastConnectedAt: lastConnectedAt.value,
    lastDisconnectedAt: lastDisconnectedAt.value,
    error: error.value,
    quality: connectionQuality.value,
    offlineMessageCount: offlineMessages.value.length
  }))

  // Actions
  const connect = async (): Promise<void> => {
    const authStore = useAuthStore()
    
    if (!authStore.isAuthenticated || !authStore.user) {
      throw new Error('User must be authenticated to connect')
    }

    if (isConnecting.value || isConnected.value) {
      return
    }

    try {
      isConnecting.value = true
      error.value = null

      // Setup status listener if not already set
      if (!statusListenerCleanup) {
        statusListenerCleanup = rsocketService.addStatusListener(handleStatusChange)
      }

      await rsocketService.connect(authStore.user.id, authStore.token!)
      
      // Connection success is handled by status listener
    } catch (err: any) {
      isConnecting.value = false
      error.value = err.message || 'Connection failed'
      throw err
    }
  }

  const disconnect = async (): Promise<void> => {
    try {
      await rsocketService.disconnect()
      
      // Cleanup status listener
      if (statusListenerCleanup) {
        statusListenerCleanup()
        statusListenerCleanup = null
      }
      
      // Reset state
      isConnected.value = false
      isConnecting.value = false
      isReconnecting.value = false
      lastDisconnectedAt.value = new Date()
      connectionQuality.value = 'offline'
    } catch (err: any) {
      console.error('Error during disconnect:', err)
    }
  }

  const reconnect = async (): Promise<void> => {
    if (isConnecting.value || isReconnecting.value) {
      return
    }

    if (!canRetry.value) {
      error.value = 'Maximum reconnection attempts reached'
      return
    }

    try {
      isReconnecting.value = true
      error.value = null
      
      await rsocketService.reconnect()
      
      // Reconnection success is handled by status listener
    } catch (err: any) {
      isReconnecting.value = false
      error.value = err.message || 'Reconnection failed'
      throw err
    }
  }

  const handleStatusChange = (event: ConnectionStatusEvent): void => {
    const { status, error: statusError, timestamp } = event

    switch (status) {
      case ConnectionStatus.CONNECTING:
        isConnecting.value = true
        isReconnecting.value = false
        error.value = null
        break

      case ConnectionStatus.CONNECTED:
        isConnected.value = true
        isConnecting.value = false
        isReconnecting.value = false
        lastConnectedAt.value = timestamp
        error.value = null
        connectionQuality.value = 'good'
        reconnectAttempts.value = 0
        
        // Sync offline messages when connected
        syncOfflineMessages()
        break

      case ConnectionStatus.RECONNECTING:
        isConnecting.value = false
        isReconnecting.value = true
        reconnectAttempts.value = rsocketService.getReconnectAttempts()
        break

      case ConnectionStatus.DISCONNECTED:
        isConnected.value = false
        isConnecting.value = false
        isReconnecting.value = false
        lastDisconnectedAt.value = timestamp
        connectionQuality.value = 'offline'
        break

      case ConnectionStatus.ERROR:
        isConnected.value = false
        isConnecting.value = false
        isReconnecting.value = false
        error.value = statusError?.message || 'Connection error'
        connectionQuality.value = 'offline'
        lastDisconnectedAt.value = timestamp
        break
    }
  }

  const cacheOfflineMessage = (messageData: any): string => {
    const messageId = `offline_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
    const offlineMessage = {
      id: messageId,
      data: messageData,
      timestamp: new Date()
    }
    
    offlineMessages.value.push(offlineMessage)
    
    // Persist to local storage
    chatStorage.addOfflineMessage(offlineMessage)
    
    return messageId
  }

  const removeOfflineMessage = (messageId: string): void => {
    const index = offlineMessages.value.findIndex(msg => msg.id === messageId)
    if (index !== -1) {
      offlineMessages.value.splice(index, 1)
      chatStorage.removeOfflineMessage(messageId)
    }
  }

  const syncOfflineMessages = async (): Promise<void> => {
    if (offlineMessages.value.length === 0) {
      return
    }

    const messagesStore = useMessagesStore()
    const messagesToSync = [...offlineMessages.value]
    
    console.log(`Syncing ${messagesToSync.length} offline messages`)

    for (const offlineMessage of messagesToSync) {
      try {
        // Try to send the cached message
        await messagesStore.sendMessage(offlineMessage.data)
        
        // Remove from offline cache on success
        removeOfflineMessage(offlineMessage.id)
        
        console.log(`Successfully synced offline message: ${offlineMessage.id}`)
      } catch (err) {
        console.error(`Failed to sync offline message ${offlineMessage.id}:`, err)
        // Keep the message in cache for next sync attempt
      }
    }
  }

  const loadOfflineMessages = (): void => {
    const cachedMessages = chatStorage.getOfflineMessages()
    offlineMessages.value = cachedMessages
  }

  const clearOfflineMessages = (): void => {
    offlineMessages.value = []
    chatStorage.clearOfflineMessages()
  }

  const updateConnectionQuality = (quality: 'good' | 'poor' | 'offline'): void => {
    connectionQuality.value = quality
  }

  const clearError = (): void => {
    error.value = null
  }

  const resetReconnectAttempts = (): void => {
    reconnectAttempts.value = 0
  }

  // Initialize offline messages on store creation
  loadOfflineMessages()

  return {
    // State
    isConnected: readonly(isConnected),
    isConnecting: readonly(isConnecting),
    isReconnecting: readonly(isReconnecting),
    reconnectAttempts: readonly(reconnectAttempts),
    maxReconnectAttempts: readonly(maxReconnectAttempts),
    lastConnectedAt: readonly(lastConnectedAt),
    lastDisconnectedAt: readonly(lastDisconnectedAt),
    error: readonly(error),
    connectionQuality: readonly(connectionQuality),
    offlineMessages: readonly(offlineMessages),
    
    // Getters
    connectionStatus,
    canRetry,
    connectionInfo,
    
    // Actions
    connect,
    disconnect,
    reconnect,
    cacheOfflineMessage,
    removeOfflineMessage,
    syncOfflineMessages,
    loadOfflineMessages,
    clearOfflineMessages,
    updateConnectionQuality,
    clearError,
    resetReconnectAttempts
  }
})